package lxs.swift.verify.objectCheck;

import lxs.swift.verify.Verifies;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Oc<T> {
    public final T value;

    protected Oc(T value) {
        this.value = value;
    }

    @SafeVarargs
    public final State all(@NotNull Check<T>... checks) {
        return new State(() -> {
            for (Check<T> check : checks) {
                if (!check.get(value)) {
                    return false;
                }
            }
            return true;
        });
    }

    @SafeVarargs
    public final State any(@NotNull Check<T>... checks) {
        return new State(() -> {
            for (Check<T> check : checks) {
                if (check.get(value)) {
                    return true;
                }
            }
            return false;
        });
    }

    public Oc<T> nullReplace(@NotNull T defaultValue) {
        Objects.requireNonNull(defaultValue, "value must not be null");
        if (value == null) {
            return new Oc<>(defaultValue);
        }
        return this;
    }

    public Oc<T> ifReplace(@NotNull Check<T> check, T newValue) {
        if (check.get(value)) {
            return new Oc<>(newValue);
        }
        return this;
    }

    public boolean isTrue(@NotNull Predicate<T> predicate) {
        return predicate.test(this.value);
    }

    public boolean isNull() {
        return Verifies.isNull(this.value);
    }

    public boolean isEq(T value) {
        return Verifies.isObjectEq(this.value, value);
    }

    public boolean isDefault() {
        return Verifies.isDefault(this.value);
    }

    public <E extends Throwable> void ifTrue(@NotNull Predicate<T> predicate, @NotNull Supplier<? extends E> supplier) throws E {
        if (!predicate.test(this.value)) {
            throw supplier.get();
        }
    }

    public void ifTrue(@NotNull Predicate<T> predicate, @NotNull String message) {
        this.ifTrue(predicate, () -> new IllegalArgumentException(message));
    }

    public <E extends Throwable> void ifNull(@NotNull Supplier<? extends E> supplier) throws E {
        this.ifTrue(Objects::isNull, supplier);
    }

    public void ifNull(@NotNull String message) {
        this.ifTrue(Objects::isNull, () -> new IllegalArgumentException(message));
    }

    public <E extends Throwable> void ifEq(T value, @NotNull Supplier<? extends E> supplier) throws E {
        this.ifTrue(v -> Verifies.isObjectEq(v, value), supplier);
    }

    public void ifEq(T value, @NotNull String message) {
        this.ifTrue(v -> Verifies.isObjectEq(v, value), () -> new IllegalArgumentException(message));
    }

    public <E extends Throwable> void ifDefault(@NotNull Supplier<? extends E> supplier) throws E {
        this.ifTrue(Verifies::isDefault, supplier);
    }

    public void ifDefault(@NotNull String message) {
        this.ifTrue(Verifies::isDefault, () -> new IllegalArgumentException(message));
    }

    public static <T> Oc<T> of(@Nullable T value) {
        return new Oc<>(value);
    }

    private interface Bool {
        boolean get();
    }

    public static class State {
        private final Bool bool;

        private State(Bool bool) {
            this.bool = bool;
        }

        public State negate() {
            return new State(() -> !bool.get());
        }

        public <E extends Throwable> void expected(@NotNull E e) throws E {
            if (!bool.get()) {
                throw e;
            }
        }

        public <E extends Throwable> void unexpected(@NotNull E e) throws E {
            if (bool.get()) {
                throw e;
            }
        }
    }

}
